#include "./BSP/ADC/adc3.h"
#include "./SYSTEM/delay/delay.h"

ADC_HandleTypeDef g_adc3_handle;        /* ADC3句柄 */

/**
 * @brief      ADC3初始化函数
 *
 * @param      无
 *
 * @note       配置ADC3的基本参数:
 *             - 时钟分频: PCLK2/4 = 84MHz/4 = 21MHz
 *             - 转换精度: 12位 (0~4095)
 *             - 数据对齐: 右对齐
 *             - 扫描模式: 禁用 (单通道转换)
 *             - 连续转换: 禁用 (单次转换)
 *             - 触发方式: 软件触发
 *             - EOC中断: 禁用
 *
 * @retval     无
 */
void adc3_init(void)
{
    ADC3_CHY_CLK_ENABLE();  /* 使能ADC3时钟 */

    /* 配置ADC3基本参数 */
    g_adc3_handle.Instance = ADC_ADCX;                                          /* ADC3外设 */
    g_adc3_handle.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV4;           /* 4分频,ADCCLK=PCLK2/4=84/4=21MHz */
    g_adc3_handle.Init.Resolution = ADC_RESOLUTION12b;                          /* 12位转换精度 */
    g_adc3_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;                         /* 数据右对齐 */
    g_adc3_handle.Init.ScanConvMode = DISABLE;                                  /* 禁用扫描模式 */
    g_adc3_handle.Init.EOCSelection = DISABLE;                                  /* 禁用EOC中断 */

    /* 配置转换模式 */
    g_adc3_handle.Init.ContinuousConvMode = DISABLE;                            /* 禁用连续转换模式 */
    g_adc3_handle.Init.NbrOfConversion = 1;                                     /* 规则序列转换数量为1 */
    g_adc3_handle.Init.DiscontinuousConvMode = DISABLE;                         /* 禁用不连续采样模式 */
    g_adc3_handle.Init.NbrOfDiscConversion = 0;                                 /* 不连续采样通道数为0 */

    /* 配置触发方式 */
    g_adc3_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;                   /* 软件触发转换 */
    g_adc3_handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;    /* 不使用外部触发边沿 */

    HAL_ADC_Init(&g_adc3_handle);   /* 初始化ADC3 */
}

/**
 * @brief      设置ADC3通道采样时间和转换顺序
 *
 * @param[in]  adc_handle  ADC句柄指针,指向ADC_HandleTypeDef结构体
 * @param[in]  ch          通道号,取值范围:
 *             @arg        ADC_CHANNEL_0~ADC_CHANNEL_17 (对应通道0~17)
 * @param[in]  rank        转换序列中的位置,取值范围:
 *             @arg        ADC_REGULAR_RANK_1~ADC_REGULAR_RANK_16 (序列位置1~16)
 * @param[in]  stime       采样时间,可选值:
 *             @arg        ADC_SAMPLETIME_3CYCLES   - 3个ADC时钟周期   (约0.14μs @21MHz)
 *             @arg        ADC_SAMPLETIME_15CYCLES  - 15个ADC时钟周期  (约0.71μs @21MHz)
 *             @arg        ADC_SAMPLETIME_28CYCLES  - 28个ADC时钟周期  (约1.33μs @21MHz)
 *             @arg        ADC_SAMPLETIME_56CYCLES  - 56个ADC时钟周期  (约2.67μs @21MHz)
 *             @arg        ADC_SAMPLETIME_84CYCLES  - 84个ADC时钟周期  (约4.00μs @21MHz)
 *             @arg        ADC_SAMPLETIME_112CYCLES - 112个ADC时钟周期 (约5.33μs @21MHz)
 *             @arg        ADC_SAMPLETIME_144CYCLES - 144个ADC时钟周期 (约6.86μs @21MHz)
 *             @arg        ADC_SAMPLETIME_480CYCLES - 480个ADC时钟周期 (约22.86μs @21MHz)
 *
 * @note       该函数用于配置指定ADC通道的采样参数
 *             - rank参数在多通道DMA模式下决定数据在缓冲区中的位置
 *             - 采样时间越长,转换精度越高,但转换速度越慢
 *             - 对于高阻抗信号源,建议使用较长的采样时间
 *
 * @retval     无
 */
void adc3_channel_set(ADC_HandleTypeDef *adc_handle, uint32_t ch, uint32_t rank, uint32_t stime)
{
    ADC_ChannelConfTypeDef adc_channel;     /* ADC通道配置结构体 */

    /* 配置ADC通道参数 */
    adc_channel.Channel = ch;               /* 设置通道号 */
    adc_channel.Rank = rank;                /* 设置转换序列位置 */
    adc_channel.SamplingTime = stime;       /* 设置采样时间 */

    HAL_ADC_ConfigChannel(adc_handle, &adc_channel);    /* 配置ADC通道 */
}

/**
 * @brief      获取ADC3指定通道的单次转换结果
 *
 * @param[in]  ch          通道号,取值范围:0~17 (对应ADC_CHANNEL_0~ADC_CHANNEL_17)
 *
 * @note       该函数执行以下操作:
 *             1. 配置指定通道,采样时间为480个ADC周期
 *             2. 启动ADC转换
 *             3. 等待转换完成(超时时间10ms)
 *             4. 读取并返回转换结果
 *
 *             转换时间计算:
 *             - 采样时间: 480个ADC周期
 *             - 转换时间: 12.5个ADC周期
 *             - 总时间: (480+12.5)/21MHz ≈ 23.5μs
 *
 * @retval     转换结果    12位ADC转换值(0~4095)
 */
uint32_t adc3_get_result(uint32_t ch)
{
    /* 配置通道参数:通道号、序列位置1、采样时间480周期 */
    adc3_channel_set(&g_adc3_handle, ch, 1, ADC_SAMPLETIME_480CYCLES);

    HAL_ADC_Start(&g_adc3_handle);                      /* 启动ADC转换 */
    HAL_ADC_PollForConversion(&g_adc3_handle, 10);      /* 轮询等待转换完成,超时10ms */

    return (uint16_t)HAL_ADC_GetValue(&g_adc3_handle);  /* 返回ADC3转换结果 */
}

/**
 * @brief      获取ADC3指定通道多次采样的平均值
 *
 * @param[in]  ch          通道号,取值范围:0~17 (对应ADC_CHANNEL_0~ADC_CHANNEL_17)
 * @param[in]  times       采样次数,建议范围:1~255
 *
 * @note       该函数通过多次采样求平均值来提高测量精度:
 *             1. 循环调用adc3_get_result()进行times次采样
 *             2. 每次采样间隔5ms,避免采样过于频繁
 *             3. 计算所有采样值的算术平均值
 *             4. 可有效减少随机噪声的影响
 *
 *             总耗时计算:
 *             - 单次转换时间: ~23.5μs
 *             - 采样间隔: 5ms
 *             - 总时间: times × (5ms + 23.5μs) ≈ times × 5ms
 *
 * @retval     平均值      多次采样的平均结果(0~4095)
 */
uint32_t adc3_get_result_average(uint32_t ch, uint8_t times)
{
    uint32_t temp_val = 0;      /* 累加值 */
    uint8_t t;                  /* 循环计数器 */

    /* 循环采样times次 */
    for (t = 0; t < times; t++)
    {
        temp_val += adc3_get_result(ch);    /* 累加采样值 */
        delay_ms(5);                        /* 采样间隔5ms */
    }

    return temp_val / times;    /* 返回平均值 */
}
